package com.fc.convert;

import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import com.fc.anno.EnumClass;
import com.fc.enums.IEnum;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

public class EnumConverter implements Converter<Object> {

    /**
     * key   -> 枚举 Class
     * value -> description -> code 的映射（导入用）
     */
    private static final Map<Class<?>, Map<String, Integer>> DESC_TO_CODE_CACHE = new ConcurrentHashMap<>();

    /**
     * key   -> 枚举 Class
     * value -> code -> description 的映射（导出用）
     */
    private static final Map<Class<?>, Map<Integer, String>> CODE_TO_DESC_CACHE = new ConcurrentHashMap<>();

    @Override
    public Class<?> supportJavaTypeKey() {
        return Object.class;   // 支持任意枚举
    }

    @Override
    public CellDataTypeEnum supportExcelTypeKey() {
        return CellDataTypeEnum.STRING;
    }

    @Override
    public Object convertToJavaData(ReadCellData<?> cellData,
                                    ExcelContentProperty contentProperty,
                                    GlobalConfiguration globalConfiguration) throws Exception {

        String cellValue = cellData.getStringValue();
        if (cellValue == null || cellValue.trim().isEmpty()) {
            return null;
        }

        Class<? extends IEnum> enumClass = getEnumClass(contentProperty);

        Map<String, Integer> descToCode = DESC_TO_CODE_CACHE
                .computeIfAbsent(enumClass, this::buildDescToCodeMap);

        Integer code = descToCode.get(cellValue.trim());
        if (code == null) {
            throw new IllegalArgumentException("找不到对应枚举描述：" + cellValue);
        }

        Method fromCode = enumClass.getDeclaredMethod("fromCode", int.class);
        return fromCode.invoke(null, code);   // 返回 Integer 或枚举都行
    }


    @Override
    public WriteCellData<?> convertToExcelData(Object value,
                                               ExcelContentProperty contentProperty,
                                               GlobalConfiguration globalConfiguration) throws Exception {

        if (value == null) {
            return new WriteCellData<>("");
        }
        Class<? extends IEnum> enumClass = getEnumClass(contentProperty);
        Map<Integer, String> codeToDesc = CODE_TO_DESC_CACHE
                .computeIfAbsent(enumClass, this::buildCodeToDescMap);
        int code;
        if (value instanceof Number) {
            code = ((Number) value).intValue();
        } else if (value instanceof IEnum) {
            code = ((IEnum) value).getCode();
        } else {
            throw new IllegalArgumentException("不支持的类型：" + value.getClass());
        }

        return new WriteCellData<>(codeToDesc.getOrDefault(code, ""));
    }


    private static Class<? extends IEnum> getEnumClass(ExcelContentProperty contentProperty) {
        /* 重点：从注解里拿到枚举类 */
        EnumClass enumClassAnno = contentProperty.getField().getAnnotation(EnumClass.class);
        if (enumClassAnno == null) {
            throw new IllegalStateException("字段必须使用 @EnumClass 指定枚举");
        }
        return enumClassAnno.value();
    }


    private Map<Integer, String> buildCodeToDescMap(Class<?> enumClass) {
        return Arrays.stream(enumClass.getEnumConstants())
                .map(o -> (IEnum) o)
                .collect(Collectors.toMap(IEnum::getCode, IEnum::getDescription));
    }

    private Map<String, Integer> buildDescToCodeMap(Class<?> enumClass) {
        return Arrays.stream(enumClass.getEnumConstants())
                .map(o -> (IEnum) o)
                .collect(Collectors.toMap(IEnum::getDescription, IEnum::getCode));
    }
}